package com.gitee.wsl.net.ssdp.discovery

import com.gitee.wsl.net.ssdp.NotifyServer
import com.gitee.wsl.net.ssdp.SSDP_PORT
import com.gitee.wsl.net.ssdp.message.SsdpMessage
import com.gitee.wsl.net.ssdp.message.SsdpMessageType
import com.gitee.wsl.net.ssdp.server.SsdpServerDelegate
import com.gitee.wsl.net.ssdp.server.getSsdpAddress
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.receiveAsFlow
import kotlinx.coroutines.flow.toList
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import org.mockito.kotlin.mock
import java.net.DatagramSocket
import java.net.InetAddress
import java.net.NetworkInterface
import kotlin.test.BeforeTest
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertNotNull

class NotifyServerTest {

    private lateinit var networkInterface: NetworkInterface
    private lateinit var address: InetAddress
    private lateinit var receivingChannel: Channel<SsdpMessage>
    private lateinit var notifyServer: NotifyServer
    private val coroutineContext = Dispatchers.IO

    @BeforeTest
    fun setUp() {
        networkInterface = NetworkInterface.getByName("lo0")    // loopback interface for testing
        address = InetAddress.getByName("239.255.255.250")      // standard SSDP multicast address
        receivingChannel = Channel()
        notifyServer = NotifyServer(
            SsdpServerDelegate(
                getSsdpAddress(networkInterface),
                networkInterface,
                SSDP_PORT,
                receivingChannel,
                "testCP",
                coroutineContext
            )
        )
    }

    @Test
    fun testInitialization() {
        assertNotNull(notifyServer)
        notifyServer.stop()
    }

    @Test
    fun testStartAndStop() = runBlocking {
        notifyServer.start()
        assertEquals(true, notifyServer.isActive())

        notifyServer.stop()
        assertEquals(false, notifyServer.isActive())
    }

    //@Test
    // TODO investigate why this test fails itself and fails other tests
    fun testSendAndReceive() = runBlocking {
        mock<DatagramSocket> { onGeneric { send(any()) } }
        notifyServer.start()
        var receivedMessage: SsdpMessage? = null
        val testMessage = SsdpMessage(SsdpMessageType.Search)

        val testJob = launch(coroutineContext) {
            receivingChannel.receiveAsFlow()
                .toList()
                .firstOrNull()
                ?.let { receivedMessage = it }
        }

        notifyServer.send(testMessage)

        assertEquals(testMessage, receivedMessage)

        notifyServer.stop()
        testJob.cancel()
    }
}
